package de.tv.weatherox;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.LineIterator;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;

import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.*;
import java.util.regex.*;

/**
 * Created by IntelliJ IDEA.
 * User: Thomas
 * Date: 14.05.11
 * Time: 08:04
 * To change this template use File | Settings | File Templates.
 */
public class WeatherForecastBuilder {
    private static URL wetterochsUrl;
    private LineIterator lineIterator;
    private List<Forecast> dailyForecasts = new ArrayList<Forecast>();
    private static final int COLUMN_DATE = 0;
    private static final int COLUMN_CLOUDINESS = 1;
    private static final int COLUMN_TEMPERATURE_MAX = 2;
    private static final int COLUMN_TEMPERATURE_MIN = 3;
    private static final int COLUMN_DEWPOINT = 4;
    private static final int COLUMN_PRECIPITATION = 5;
    private static final int COLUMN_WIND = 6;
    private static final int COLUMN_DAWN = 7;
    private static final int COLUMN_DUSK = 8;
    private static final int COLUMN_MOON = 9;

    public static final Logger LOGGER = Logger.getLogger(WeatherForecastBuilder.class.getName());

    public List<Forecast> getDailyForecasts() {
        return dailyForecasts;
    }

    private List<String> dailyForecastHtmlColumns = new ArrayList<String>();

    private WeatherForecastBuilder() {

    }

    static {
        try {
            WeatherForecastBuilder.wetterochsUrl = new URL("http://www.wetterochs.de/wetter/delphi/mrf.html");
        } catch (MalformedURLException ex) {
            LOGGER.log(Level.SEVERE, "No valid Url: " + wetterochsUrl, ex);
        }
    }

    public static WeatherForecastBuilder create(WetterochsPage page) throws IOException {
        WeatherForecastBuilder ret = new WeatherForecastBuilder();
        try {
            ret.lineIterator = IOUtils.lineIterator(page.getInputStream(), "UTF-8");
            ret.moveIteratorToFirstForecastRow();
            while (ret.hasNextDailyForecast()) {
                ret.dailyForecasts.add(ret.getNextDailyForecast());
            }
            return ret;
        } finally {
            LOGGER.info("Closing InputStream");
            LineIterator.closeQuietly(ret.lineIterator);
        }
    }

    private Forecast getNextDailyForecast() {
        Forecast ret = new Forecast();
        String date = stripHtmlCellCode(this.dailyForecastHtmlColumns.get(COLUMN_DATE));
        ret.setDateAsString(date);
        String cloudinessImage = stripHtmlCellCode(this.dailyForecastHtmlColumns.get(COLUMN_CLOUDINESS));
        ret.setCloudiness(ForecastAttributeTransformer.transformCloudinessImageToValue(cloudinessImage));
        String temperatureMax = stripHtmlCellCode(this.dailyForecastHtmlColumns.get(COLUMN_TEMPERATURE_MAX));
        ret.setTemperatureMax(ForecastAttributeTransformer.transformTemperatureStringToInt(temperatureMax));
        String temperatureMin = stripHtmlCellCode(this.dailyForecastHtmlColumns.get(COLUMN_TEMPERATURE_MIN));
        ret.setTemperatureMin(ForecastAttributeTransformer.transformTemperatureStringToInt(temperatureMin));
        String dewpoint = stripHtmlCellCode(this.dailyForecastHtmlColumns.get(COLUMN_DEWPOINT));
        ret.setDewpoint(ForecastAttributeTransformer.transformTemperatureStringToInt(dewpoint));
        String precipitation = stripHtmlCellCode(this.dailyForecastHtmlColumns.get(COLUMN_PRECIPITATION));
        if (!precipitation.endsWith("mm")) {
            precipitation = precipitation.substring(precipitation.length() - 1) + " " + precipitation.substring(0, precipitation.length() - 1).trim();
        }
        ret.setPrecipitation(ForecastAttributeTransformer.transformMillimeterStringToInt(precipitation));
        String wind = stripHtmlCellCode(this.dailyForecastHtmlColumns.get(COLUMN_WIND));
        if (!StringUtils.equalsIgnoreCase(wind, "Windstill")) {
            ret.setWindForce(ForecastAttributeTransformer.transformToWindForce(wind));
            ret.setWindDirection(ForecastAttributeTransformer.transformToWindDirection(wind));
        }
        String dawn = stripHtmlCellCode(this.dailyForecastHtmlColumns.get(COLUMN_DAWN));
        ret.setDawn(dawn);
        String dusk = stripHtmlCellCode(this.dailyForecastHtmlColumns.get(COLUMN_DUSK));
        ret.setDusk(dusk);
        String moon = stripHtmlCellCode(this.dailyForecastHtmlColumns.get(COLUMN_MOON));
        ret.setMoon(ForecastAttributeTransformer.transformMoonImageToMoonPhase(moon));

        setIdAndDateFromDateAsString(ret);
        return ret;
    }

    private String stripHtmlCellCode(String htmlCellCode) {
        Pattern pattern = Pattern.compile("<td[^<]*>");
        Matcher matcher = pattern.matcher(htmlCellCode);
        String ret = matcher.replaceFirst("");

        pattern = Pattern.compile("</td>");
        matcher = pattern.matcher(ret);
        ret = matcher.replaceFirst("");

        ret = StringEscapeUtils.unescapeHtml(ret);

        return ret;
    }

    private boolean hasNextDailyForecast() {
        boolean ret = false;
        this.dailyForecastHtmlColumns.clear();
        while (this.lineIterator.hasNext()) {
            String line = (String) this.lineIterator.next();
            if (line.contains("<td class=mrfx>")) {
                ret = true;
                this.dailyForecastHtmlColumns.add(line);
                for (int i = 0; i < 9; i++) {
                    line = (String) this.lineIterator.next();
                    this.dailyForecastHtmlColumns.add(line);
                }
                break;
            }

        }
        return ret;
    }

    private void moveIteratorToFirstForecastRow() {
        boolean foundStart = false;
        while (this.lineIterator.hasNext()) {
            String line = (String) lineIterator.next();
            if (!foundStart && line.contains("<th class=mrfx>")) {
                foundStart = true;
            }
            if (foundStart && line.equals("</tr>")) {
                break;
            }
        }
    }

    private void setIdAndDateFromDateAsString(Forecast forecast) {
        try {
            String dayAndMonth = forecast.getDateAsString().substring(forecast.getDateAsString().length() - 6);
            Calendar forecastDate = new GregorianCalendar();
            forecastDate.setTime(new SimpleDateFormat("dd.MM.").parse(dayAndMonth));
            Calendar now = new GregorianCalendar();
            now.setTime(new Date(System.currentTimeMillis()));
            int year = now.get(Calendar.YEAR);
            if ((now.get(Calendar.MONTH) == Calendar.JANUARY) && forecastDate.get(Calendar.MONTH) == Calendar.DECEMBER) {
                year--;
            }
            forecastDate.set(Calendar.YEAR, year);
            forecast.setDate(forecastDate.getTime());
            forecast.setId(Long.parseLong(new SimpleDateFormat("yyyyMMdd").format(forecast.getDate())));
        } catch (Exception ex) {
            LOGGER.log(Level.WARNING, "Error while parsing date", ex);
        }
    }
}
